领域驱动设计在理财系统设计中的应用初探
需求不但多变,而且经常是不可能第一次就能掌握,比如理财系统建设时,谁也想不到监管政策的变化、互联网业务的拓展,很难在理财领域一次就梳理全所有已知和未知的需求范围。领域驱动设计提出了领域模型概念,统一了分析和设计编程,使得软件能够更灵活快速跟随需求变化。领域模型准确反映了业务语言,能够快速适应需求变化的软件复用。
本文结合领域驱动设计思想,检视了理财系统的设计,尝试利用领域模型和边界划分分析子系统或模块设计问题,为今后应对新的业务变化时的系统设计探索一条解决路径。
什么是领域驱动设计
领域驱动设计(Domain-driven Design,简称DDD)的概念是2004年Evic Evans在他的著作《领域驱动设计:软件核心复杂性应对之道》(英文名《Domain-Driven Design : Tackling Complexity in the Heart of Software》)中提出的。
领域驱动设计在近些年逐渐成为业界的关注热点,其原因主要是随着信息化在深度和广度方面不断推进,信息处理系统日益复杂,而领域驱动设计提出的很多理念和方法为应对以上复杂性提供了很好的解决思路。在早期的时候,业务逻辑简单,只要增、删、改、查(CRUD)即可。一个模块包含多个功能。以典型的电商平台中的订单服务为例,把下订单、支付、售后、评价等都放在里面,早期的时候功能很简单,只维护订单服务,只需要通过CRUD操作订单大表即可。但是随着业务的发展,当只想改支付功能或评价功能时,就不能轻易做改动了,因为改动支付的时候,有可能影响到售后。
领域驱动设计提出以领域驱动设计的思想进行业务架构设计(根据业务需求设计业务模块)、系统架构设计(设计系统、子系统)、技术架构设计(采用技术及相关框架),将业务架构映射到系统架构上,业务需求变化时,系统架构也能随之变化。
领域驱动设计分为战略设计和战术设计两个层面。在战略设计层面提出了域、子域、限界上下文等重要概念。在战术设计层面提出了实体、值对象、领域服务、领域事件、聚合、工厂、资源库等重要概念。如图1所示:
图1 领域驱动设计
战略设计部分指导如何拆分一个复杂的系统,战术部分指导对于拆分出来的单个子系统如何进行落地,在落地过程中应该遵循哪些原则。
领域驱动设计的核心是边界
无论是从宏观(战略)还是在微观(战术)层次,领域驱动设计的核心思想是对边界的划分与控制。
第一重边界。从分析需求开始,需要通过确定项目的愿景与目标,划定问题空间,由此确定核心子领域、通用子领域与支撑子领域。通过这些边界的确定,帮助团队看清主次,理清了问题域中领域逻辑的优先级,促使团队在宏观层次的全局分析阶段将设计的注意力放在领域和对领域模型的理解上,满足领域驱动设计要求。
第二重边界。进入解决方案空间,战略设计获得的限界上下文。通过它可以有效地降低系统规模,无论是在业务领域,还是架构设计,或者团队协作方面,限界上下文建立的边界都成为了重要的约束力,边界内外可以形成两个不同的世界。暴露在限界上下文边界外部的是远程服务或应用服务,每个服务都提供了完整的业务功能,并通过相对稳定的契约来展现服务,由此确定限界上下文之间的协作方式。在限界上下文边界之内,可以根据不同的需求场景,使用自己独特的设计与实现体系。
第三重边界。在限界上下文内部,基础设施层、应用层与领域层之间的隔离。如果以六边形架构来观察这种层与层之间的隔离,体现的仍然是一种内外隔离,应用层形成了一种保护层,有效地隔离了业务复杂度与技术复杂度。将领域层作为整个系统稳定而内聚的核心,是领域驱动设计的关键特征。这也是软件设计的核心思想,即分离变与不变。领域内核中的领域模型具有一种本质的不变性,只要我们将领域逻辑剖析清楚,该模型就能保证相对的稳定性。若能再正确地识别可能的扩展与变化,加以抽象与封装,就能维持领域模型绝对的稳定性。
第四重边界。若要维持领域内核的稳定性,高内聚与低耦合是其根本要则。虽然职责分配的不合理在应用层边界的隔离下可以将影响降到最低,但总是在调整与修改的领域模型无法维护领域概念的完整性和一致性。为此,领域模型引入了聚合这一最小的设计单元,它从完整性与一致性对领域模型进行了有效的隔离。领域驱动设计为聚合规定了严谨的设计约束,使得整个领域模型的对象图不再变得散漫,彼此之间的协作也有了严格的边界控制。聚合设计原则要求聚合之间通过ID进行关联,避免了聚合根实体之间的引用依赖,也不会受到限界上下文边界变化的影响。
图2 领域驱动设计的四重边界
综上所述,领域驱动设计在各个层次提出的核心模式具有不同的粒度和设计关注点,但本质都在于确定边界。毕竟,随着规模的扩大,一个没有边界的系统终究会变得越来越混乱,架构没有清晰的层次,职责缺乏合理的分配,代码变得不可阅读和维护,最终形成一种无序设计。
理财系统的现状与问题
1
现状
理财系统目前包含3个子系统:主系统、余额宝货币TA子系统(含零钱通货币TA模块)和通用货币TA子系统。如图3所示。
图3 理财系统结构
主系统中有销售模块、净值TA模块、产品管理模块、系统运营管理模块和报表查询模块,其中销售模块和净值TA模块强耦合在主系统中,模块间没有明确的边界。产品管理模块、系统运营模块和报表查询模块是3个子系统共用的,以内部交易方式调用。
余额宝货币TA子系统对接余额宝行外代销,零钱通货币TA模块对接零钱通行外代销,通用货币TA子系统对接除余额宝、零钱通以外的其他货币产品的行外代销。
2
存在的问题
理财系统存在以下两方面的问题:
一是在主系统中销售模块和TA模块强耦合,难以适应监管变化。理财系统总体架构设计阶段,主要从交易处理复杂度、成本和工期考虑,决定将销售模块和TA模块耦合。但随着理财销售新规的出台,理财的销售与TA需要进行隔离,销售端归属母行建设和管理,TA端归属理财子公司建设和管理。
二是TA系统间没有有机整合,各自独立存在。每增加一个特殊销售渠道,就要建设一个完整的TA子系统。重复建设,系统整体庞杂。
基于领域驱动设计理论的分析
1
理财系统应用架构设想
出现上章中提到的第一个问题,原因是在理财系统设计论证阶段,主要强调了技术实现方面的便利性,而忽视了业务领域边界。对应到本文第二章所归纳的领域驱动设计所强调的四大边界,就是没有正确识别出第一重边界和第二重边界,即各子领域的划分。销售和TA系统是理财系统中两个完全不同的业务域,不能基于技术考虑(如数据关联、降低交易数)等考虑,将二者强制耦合在一块。正确的做法应该是合理划分二者界限,在此基础上,规划好具有普适性的交互接口,通过接口交互,共同完成业务功能。
导致上章中提到的第二个问题,是在TA模块设计中没有做好顶层规划,没有分离“变”与“不变”。对应到本文第二章所归纳的领域驱动设计所强调的四大边界,就是没有正确识别出第三重边界和第四重边界。在TA子系统中,“变”的部分是销售渠道所带来的业务规则不同,其他都是不变的,正确的做法应该是将“不变”的部分抽取出来,形成独立的服务,其保持相对稳定性,避免重复建设。
基于以上分析,理财系统的更为理想的可为如下:
图4 理财应用架构设想
2
领域驱动设计与敏捷开发结合
领域驱动设计理论中的边界理论,可以和行里目前所倡导的敏捷开发模式很好得结合起来。
敏捷开发以用户的需求进化为核心,采用迭代、循序渐进的方法进行软件开发。在敏捷开发中,软件项目在构建初期被切分成多个子项目,各个子项目的成果都经过测试,具备可视、可集成和可运行使用的特征。换言之,就是把一个大项目分为多个相互联系、独立的小项目,并分别完成,在此过程中软件一直处于可使用状态。敏捷的过程,就是将大需求拆小的过程。一旦拆好了以后,可以组建小的团队针对每一个小需求进行软件设计、研发。经过敏捷的过程,整个系统是由一个一个小的模块叠加而成的,是从部分到整体的过程。
如上所述,如何将大项目拆分成多个小项目,关系敏捷开发是否有效开展的重要一环。而利用领域驱动设计把整体业务需求按照业务领域划分出不同的子域,每个子域可以作为一个独立的子项目,并且可选择优先实现核心子域,即实现系统核心、基本功能,让系统跑起来。再进一步,在子域实现过程中,识别出“变”与“不变”的两部分,先期实现“不变”的部分,然后迭代实现“变”的部分。
理财系统的日常需求多为复杂的业务需求,利用领域驱动设计思想可以更好的解决敏捷开发中业务不确定性的影响,既可以快速迭代,又可以最大限度的避免需求波动带来的推倒重来风险。
总结
各种软件设计理论本质上没有好与坏之分,只有哪个更适合之分,而领域驱动设计适用于复杂业务系统设计,有助于提升系统对需求变化的快速响应能力。本文利用领域驱动设计中核心的边界理念对理财系统设计分析进行了初步尝试。领域驱动设计具体要怎么落地,是否会引入其他未知问题,还有待于在实践中去探索。
作者 | 郭 纯
视觉 | 王朋玉
统筹 | 郑 洁